# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime, timedelta
import tkinter as tk
from tkinter import ttk, messagebox
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
import matplotlib
from mpl_toolkits.mplot3d import Axes3D
import os
import sys
matplotlib.use('TkAgg')

# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False


class CometBrightnessApp:
    def __init__(self, root):
        self.root = root
        self.root.title("彗星亮度预测工具 v6.1")
        self.root.geometry("1400x900")

        # 初始化变量
        self.current_dates = None
        self.mpc_m_pred = None
        self.cobs_m_pred = None
        self.astro_m_pred = None
        self.r_pred = None
        self.delta_pred = None
        self.orbit_points = None

        # 子窗口引用
        self.brightness_window = None
        self.distance_window = None
        self.orbit_3d_window = None

        # 设置UI
        self.setup_ui()
        self.fill_sample_data()

    def setup_ui(self):
        """设置用户界面"""
        # 主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 配置三栏布局
        main_frame.columnconfigure(0, weight=40, uniform="col")
        main_frame.columnconfigure(1, weight=40, uniform="col")
        main_frame.columnconfigure(2, weight=20, uniform="col")

        # 左侧控制区域
        left_frame = ttk.LabelFrame(main_frame, text="彗星参数设置", padding="10")
        left_frame.grid(row=0, column=0, sticky="nsew", padx=(0, 5))

        # 中间结果显示区域
        middle_frame = ttk.LabelFrame(main_frame, text="预测结果", padding="10")
        middle_frame.grid(row=0, column=1, sticky="nsew", padx=5)

        # 右侧控制区域
        right_frame = ttk.LabelFrame(main_frame, text="窗口控制", padding="10")
        right_frame.grid(row=0, column=2, sticky="nsew", padx=(5, 0))

        # 设置行权重
        main_frame.rowconfigure(0, weight=1)

        # 设置各栏内容
        self.setup_left_frame(left_frame)
        self.setup_middle_frame(middle_frame)
        self.setup_right_frame(right_frame)

    def setup_left_frame(self, parent):
        """设置左侧栏内容"""
        # 彗星选择
        comet_frame = ttk.LabelFrame(parent, text="彗星选择", padding="10")
        comet_frame.pack(fill=tk.X, pady=5)

        ttk.Label(comet_frame, text="选择彗星:").grid(
            row=0, column=0, padx=5, pady=2, sticky=tk.W)
        self.comet_var = tk.StringVar()
        comet_combo = ttk.Combobox(
            comet_frame, textvariable=self.comet_var, width=25)
        comet_combo['values'] = (
            "323P-B/SOHO", "240P/NEAT", "24P/Schaumasse",
            "C/2025 A6 (Lemmon)", "C/2025 R2 (SWAN)", "C/2025 K1 (ATLAS)",
            "210P/Christensen", "3I/ATLAS", "C/2024 E1 (Wierzchos)"
        )
        comet_combo.grid(row=0, column=1, padx=5, pady=2)
        comet_combo.bind('<<ComboboxSelected>>', self.on_comet_selected)

        # 彗星信息
        info_frame = ttk.LabelFrame(parent, text="彗星信息", padding="10")
        info_frame.pack(fill=tk.X, pady=5)

        ttk.Label(info_frame, text="彗星名称:").grid(
            row=0, column=0, padx=5, pady=2, sticky=tk.W)
        self.name_entry = ttk.Entry(info_frame, width=25)
        self.name_entry.grid(row=0, column=1, padx=5, pady=2)

        # 轨道元素
        orbit_frame = ttk.LabelFrame(
            parent, text="轨道元素 (来源: MPC星历表服务)", padding="10")
        orbit_frame.pack(fill=tk.X, pady=5)

        orbit_params = [
            ("q (近日点距离 AU)", "perihelion_distance"),
            ("a (半长轴 AU)", "semi_major_axis"),
            ("e (偏心率)", "eccentricity"),
            ("ω (近日点幅角 °)", "arg_perihelion"),
            ("Ω (升交点赤经 °)", "long_asc_node"),
            ("i (轨道倾角 °)", "inclination"),
            ("P (轨道周期 年)", "orbital_period"),
            ("T (近日点时间)", "perihelion_time"),
            ("历元时间", "epoch_time")
        ]

        self.orbit_entries = {}
        for i, (label, key) in enumerate(orbit_params):
            ttk.Label(orbit_frame, text=label).grid(
                row=i, column=0, padx=5, pady=1, sticky=tk.W)
            entry = ttk.Entry(orbit_frame, width=15)
            entry.grid(row=i, column=1, padx=5, pady=1)
            self.orbit_entries[key] = entry

        # 模型选择
        model_frame = ttk.LabelFrame(parent, text="模型选择", padding="10")
        model_frame.pack(fill=tk.X, pady=5)

        self.use_mpc_var = tk.BooleanVar(value=True)
        self.use_cobs_var = tk.BooleanVar(value=True)
        self.use_astro_var = tk.BooleanVar(value=True)

        ttk.Checkbutton(model_frame, text="使用MPC模型", variable=self.use_mpc_var).pack(
            anchor=tk.W, padx=5, pady=2)
        ttk.Checkbutton(model_frame, text="使用COBS模型", variable=self.use_cobs_var).pack(
            anchor=tk.W, padx=5, pady=2)
        ttk.Checkbutton(model_frame, text="使用ASTRO模型", variable=self.use_astro_var).pack(
            anchor=tk.W, padx=5, pady=2)

        # 星等参数
        mag_frame = ttk.LabelFrame(parent, text="星等参数 (三种预测模型)", padding="10")
        mag_frame.pack(fill=tk.X, pady=5)

        # MPC模型
        mpc_frame = ttk.Frame(mag_frame)
        mpc_frame.pack(fill=tk.X, pady=2)
        ttk.Label(mpc_frame, text="MPC模型 H:").pack(side=tk.LEFT, padx=5)
        self.mpc_H_entry = ttk.Entry(mpc_frame, width=8)
        self.mpc_H_entry.pack(side=tk.LEFT, padx=5)
        ttk.Label(mpc_frame, text="n:").pack(side=tk.LEFT, padx=5)
        self.mpc_n_entry = ttk.Entry(mpc_frame, width=8)
        self.mpc_n_entry.pack(side=tk.LEFT, padx=5)

        # COBS模型
        cobs_frame = ttk.Frame(mag_frame)
        cobs_frame.pack(fill=tk.X, pady=2)
        ttk.Label(cobs_frame, text="COBS模型 H:").pack(side=tk.LEFT, padx=5)
        self.cobs_H_entry = ttk.Entry(cobs_frame, width=8)
        self.cobs_H_entry.pack(side=tk.LEFT, padx=5)
        ttk.Label(cobs_frame, text="n:").pack(side=tk.LEFT, padx=5)
        self.cobs_n_entry = ttk.Entry(cobs_frame, width=8)
        self.cobs_n_entry.pack(side=tk.LEFT, padx=5)

        # ASTRO模型
        astro_frame = ttk.Frame(mag_frame)
        astro_frame.pack(fill=tk.X, pady=2)
        ttk.Label(astro_frame, text="ASTRO模型 H:").pack(side=tk.LEFT, padx=5)
        self.astro_H_entry = ttk.Entry(astro_frame, width=8)
        self.astro_H_entry.pack(side=tk.LEFT, padx=5)
        ttk.Label(astro_frame, text="n:").pack(side=tk.LEFT, padx=5)
        self.astro_n_entry = ttk.Entry(astro_frame, width=8)
        self.astro_n_entry.pack(side=tk.LEFT, padx=5)

    def setup_middle_frame(self, parent):
        """设置中间栏内容"""
        # 创建滚动区域
        canvas = tk.Canvas(parent)
        scrollbar = ttk.Scrollbar(
            parent, orient="vertical", command=canvas.yview)
        scrollable_frame = ttk.Frame(canvas)

        scrollable_frame.bind("<Configure>", lambda e: canvas.configure(
            scrollregion=canvas.bbox("all")))
        canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
        canvas.configure(yscrollcommand=scrollbar.set)

        canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # 结果显示
        self.info_labels = {}
        result_data = [
            ("轨道类型", "待计算"),
            ("MPC最亮星等", "待计算"), ("MPC最亮日期", "待计算"),
            ("COBS最亮星等", "待计算"), ("COBS最亮日期", "待计算"),
            ("ASTRO最亮星等", "待计算"), ("ASTRO最亮日期", "待计算"),
            ("近日点日期", "待计算"), ("预测总天数", "待计算"),
            ("近地点距离", "待计算"),
            ("今日MPC星等", "待计算"), ("今日COBS星等", "待计算"), ("今日ASTRO星等", "待计算"),
            ("今日日彗距", "待计算"), ("今日地彗距", "待计算"),
            ("30天后MPC星等", "待计算"), ("30天后COBS星等", "待计算"), ("30天后ASTRO星等", "待计算"),
            ("30天后日彗距", "待计算"), ("30天后地彗距", "待计算"),
            ("60天后MPC星等", "待计算"), ("60天后COBS星等", "待计算"), ("60天后ASTRO星等", "待计算"),
            ("60天后日彗距", "待计算"), ("60天后地彗距", "待计算"),
            ("彗发长度 (km)", "待计算"), ("彗发长度 (°)", "待计算"),
            ("彗尾长度 (km)", "待计算"), ("彗尾长度 (°)", "待计算")
        ]

        for i, (label, value) in enumerate(result_data):
            frame = ttk.Frame(scrollable_frame)
            frame.pack(fill=tk.X, pady=2)

            ttk.Label(frame, text=label + ":", width=20,
                      anchor="e").pack(side=tk.LEFT, padx=5)
            value_label = ttk.Label(frame, text=value, width=20, anchor="w")
            value_label.pack(side=tk.LEFT, padx=5)
            self.info_labels[label] = value_label

    def setup_right_frame(self, parent):
        """设置右侧栏内容 - 修复emoji字符问题"""
        # 窗口控制按钮 - 移除emoji字符
        ttk.Button(parent, text="打开亮度图表窗口", command=self.open_brightness_window).pack(
            pady=10, fill=tk.X)
        ttk.Button(parent, text="打开距离图表窗口", command=self.open_distance_window).pack(
            pady=10, fill=tk.X)
        ttk.Button(parent, text="打开3D轨道窗口", command=self.open_orbit_3d_window).pack(
            pady=10, fill=tk.X)
        ttk.Button(parent, text="关闭所有窗口", command=self.close_all_windows).pack(
            pady=10, fill=tk.X)

        # 预测控制
        pred_frame = ttk.LabelFrame(parent, text="预测控制", padding="10")
        pred_frame.pack(fill=tk.X, pady=10)

        self.pred_mode = tk.StringVar(value="days")

        ttk.Radiobutton(pred_frame, text="按天数预测", variable=self.pred_mode,
                        value="days").pack(anchor=tk.W, pady=2)
        ttk.Radiobutton(pred_frame, text="按日期范围预测", variable=self.pred_mode,
                        value="dates").pack(anchor=tk.W, pady=2)

        self.days_frame = ttk.Frame(pred_frame)
        self.days_frame.pack(fill=tk.X, pady=5)

        ttk.Label(self.days_frame, text="预测天数:").pack(side=tk.LEFT, padx=5)
        self.pred_days_entry = ttk.Entry(self.days_frame, width=10)
        self.pred_days_entry.pack(side=tk.LEFT, padx=5)
        self.pred_days_entry.insert(0, "1825")

        self.dates_frame = ttk.Frame(pred_frame)

        ttk.Label(self.dates_frame, text="开始日期:").pack(side=tk.LEFT, padx=5)
        self.start_date_entry = ttk.Entry(self.dates_frame, width=12)
        self.start_date_entry.pack(side=tk.LEFT, padx=5)

        ttk.Label(self.dates_frame, text="结束日期:").pack(side=tk.LEFT, padx=5)
        self.end_date_entry = ttk.Entry(self.dates_frame, width=12)
        self.end_date_entry.pack(side=tk.LEFT, padx=5)

        self.pred_mode.trace_add('write', self.toggle_prediction_mode)

        # 未来天数选择
        future_frame = ttk.Frame(pred_frame)
        future_frame.pack(fill=tk.X, pady=5)
        ttk.Label(future_frame, text="未来预测:").pack(side=tk.LEFT, padx=5)
        self.future_days_option = tk.StringVar(value="30天")
        future_combo = ttk.Combobox(
            future_frame, textvariable=self.future_days_option, width=10)
        future_combo['values'] = ('30天', '60天', '自定义')
        future_combo.pack(side=tk.LEFT, padx=5)
        future_combo.bind('<<ComboboxSelected>>', self.on_future_days_selected)

        self.custom_days_frame = ttk.Frame(pred_frame)
        ttk.Label(self.custom_days_frame, text="自定义天数:").pack(
            side=tk.LEFT, padx=5)
        self.custom_days_entry = ttk.Entry(self.custom_days_frame, width=8)
        self.custom_days_entry.pack(side=tk.LEFT, padx=5)

        # 操作按钮 - 移除emoji字符
        action_frame = ttk.Frame(pred_frame)
        action_frame.pack(fill=tk.X, pady=10)
        ttk.Button(action_frame, text="开始预测", command=self.run_prediction).pack(
            side=tk.LEFT, padx=5, expand=True)
        ttk.Button(action_frame, text="重置参数", command=self.clear_entries).pack(
            side=tk.LEFT, padx=5, expand=True)

        # 查询日期
        query_frame = ttk.Frame(pred_frame)
        query_frame.pack(fill=tk.X, pady=5)
        ttk.Label(query_frame, text="查询日期:").pack(side=tk.LEFT, padx=5)
        self.query_date_entry = ttk.Entry(query_frame, width=15)
        self.query_date_entry.pack(side=tk.LEFT, padx=5)
        self.query_date_entry.insert(0, datetime.now().strftime('%Y-%m-%d'))

        ttk.Button(pred_frame, text="查询亮度",
                   command=self.query_brightness).pack(pady=5, fill=tk.X)
        ttk.Button(pred_frame, text="显示未来数据",
                   command=self.show_future_data).pack(pady=5, fill=tk.X)

    def on_future_days_selected(self, event):
        """未来天数选择事件"""
        option = self.future_days_option.get()
        if option == '自定义':
            self.custom_days_frame.pack(fill=tk.X, pady=5)
        else:
            self.custom_days_frame.pack_forget()

    def toggle_prediction_mode(self, *args):
        """切换预测模式"""
        if self.pred_mode.get() == "days":
            self.days_frame.pack(fill=tk.X, pady=5)
            self.dates_frame.pack_forget()
        else:
            self.days_frame.pack_forget()
            self.dates_frame.pack(fill=tk.X, pady=5)
            if not self.start_date_entry.get():
                self.start_date_entry.insert(
                    0, datetime.now().strftime('%Y-%m-%d'))
            if not self.end_date_entry.get():
                future_date = (datetime.now() +
                               timedelta(days=1825)).strftime('%Y-%m-%d')
                self.end_date_entry.insert(0, future_date)

    def on_comet_selected(self, event):
        """彗星选择事件处理"""
        comet_name = self.comet_var.get()
        self.fill_comet_data(comet_name)

    def fill_sample_data(self):
        """填充示例数据"""
        self.comet_var.set("323P-B/SOHO")
        self.fill_comet_data("323P-B/SOHO")

    def fill_comet_data(self, comet_name):
        """根据选择的彗星填充数据"""
        comet_data = {
            "323P-B/SOHO": {
                "name": "323P-B/SOHO",
                "orbit": {
                    "perihelion_distance": "0.040063",
                    "semi_major_axis": "2.890131",
                    "eccentricity": "0.986138",
                    "arg_perihelion": "353.9691",
                    "long_asc_node": "323.4650",
                    "inclination": "5.4627",
                    "orbital_period": "4.9135",
                    "perihelion_time": "2025-12-16 05:37:06",
                    "epoch_time": "2025-10-06"
                },
                "model_params": {
                    "use_mpc": True,
                    "use_cobs": True,
                    "use_astro": True,
                    "mpc_H": "20.0",
                    "mpc_n": "10.0",
                    "cobs_H": "19.5",
                    "cobs_n": "9.5",
                    "astro_H": "18.5",
                    "astro_n": "9.0"
                }
            },
            "240P/NEAT": {
                "name": "240P/NEAT",
                "orbit": {
                    "perihelion_distance": "2.121663",
                    "semi_major_axis": "3.860939",
                    "eccentricity": "0.450480",
                    "arg_perihelion": "352.0765",
                    "long_asc_node": "74.9114",
                    "inclination": "23.5370",
                    "orbital_period": "7.5868",
                    "perihelion_time": "2025-12-19 22:41:13",
                    "epoch_time": "2025-10-06"
                },
                "model_params": {
                    "use_mpc": False,
                    "use_cobs": True,
                    "use_astro": True,
                    "mpc_H": "",
                    "mpc_n": "",
                    "cobs_H": "11.0",
                    "cobs_n": "1.8",
                    "astro_H": "10.5",
                    "astro_n": "1.5"
                }
            },
            "24P/Schaumasse": {
                "name": "24P/Schaumasse",
                "orbit": {
                    "perihelion_distance": "1.183913",
                    "semi_major_axis": "4.058333",
                    "eccentricity": "0.708276",
                    "arg_perihelion": "58.4855",
                    "long_asc_node": "78.2724",
                    "inclination": "11.5023",
                    "orbital_period": "8.1760",
                    "perihelion_time": "2026-01-08 08:15:47",
                    "epoch_time": "2025-10-06"
                },
                "model_params": {
                    "use_mpc": True,
                    "use_cobs": True,
                    "use_astro": True,
                    "mpc_H": "6.5",
                    "mpc_n": "35.0",
                    "cobs_H": "6.5",
                    "cobs_n": "14.0",
                    "astro_H": "6.5",
                    "astro_n": "14.0"
                }
            },
            "C/2025 A6 (Lemmon)": {
                "name": "C/2025 A6 (Lemmon)",
                "orbit": {
                    "perihelion_distance": "0.529889",
                    "semi_major_axis": "121.283818",
                    "eccentricity": "0.995631",
                    "arg_perihelion": "132.9699",
                    "long_asc_node": "108.0978",
                    "inclination": "143.6635",
                    "orbital_period": "1335.7395",
                    "perihelion_time": "2025-11-08 12:53:59",
                    "epoch_time": "2025-10-04"
                },
                "model_params": {
                    "use_mpc": True,
                    "use_cobs": True,
                    "use_astro": True,
                    "mpc_H": "13.2",
                    "mpc_n": "10",
                    "cobs_H": "6.9",
                    "cobs_n": "3.9",
                    "astro_H": "13.2",
                    "astro_n": "10"
                }
            },
            "C/2025 R2 (SWAN)": {
                "name": "C/2025 R2 (SWAN)",
                "orbit": {
                    "perihelion_distance": "0.504053",
                    "semi_major_axis": "",
                    "eccentricity": "0.993413",
                    "arg_perihelion": "308.3194",
                    "long_asc_node": "335.3460",
                    "inclination": "4.4736",
                    "orbital_period": "",
                    "perihelion_time": "2025-09-12 19:55:55",
                    "epoch_time": "2025-10-04"
                },
                "model_params": {
                    "use_mpc": True,
                    "use_cobs": True,
                    "use_astro": True,
                    "mpc_H": "12.4",
                    "mpc_n": "10",
                    "cobs_H": "8.8",
                    "cobs_n": "2.0",
                    "astro_H": "12.4",
                    "astro_n": "10"
                }
            },
            "C/2025 K1 (ATLAS)": {
                "name": "C/2025 K1 (ATLAS)",
                "orbit": {
                    "perihelion_distance": "0.334197",
                    "semi_major_axis": "",
                    "eccentricity": "1.000263",
                    "arg_perihelion": "271.0256",
                    "long_asc_node": "97.5572",
                    "inclination": "147.8645",
                    "orbital_period": "",
                    "perihelion_time": "2025-10-08 10:31:34",
                    "epoch_time": "2025-10-04"
                },
                "model_params": {
                    "use_mpc": True,
                    "use_cobs": True,
                    "use_astro": True,
                    "mpc_H": "12.2",
                    "mpc_n": "10",
                    "cobs_H": "12.4",
                    "cobs_n": "3.0",
                    "astro_H": "12.2",
                    "astro_n": "10"
                }
            },
            "210P/Christensen": {
                "name": "210P/Christensen",
                "orbit": {
                    "perihelion_distance": "0.524416",
                    "semi_major_axis": "3.160446",
                    "eccentricity": "0.834069",
                    "arg_perihelion": "345.9494",
                    "long_asc_node": "93.7975",
                    "inclination": "10.2873",
                    "orbital_period": "5.6188",
                    "perihelion_time": "2025-11-22 17:25:43",
                    "epoch_time": "2025-10-04"
                },
                "model_params": {
                    "use_mpc": True,
                    "use_cobs": True,
                    "use_astro": True,
                    "mpc_H": "13.5",
                    "mpc_n": "10",
                    "cobs_H": "13.5",
                    "cobs_n": "4.0",
                    "astro_H": "13.5",
                    "astro_n": "10"
                }
            },
            "3I/ATLAS": {
                "name": "3I/ATLAS",
                "orbit": {
                    "perihelion_distance": "1.356324",
                    "semi_major_axis": "",
                    "eccentricity": "6.138574",
                    "arg_perihelion": "128.0125",
                    "long_asc_node": "322.1574",
                    "inclination": "175.1131",
                    "orbital_period": "",
                    "perihelion_time": "2025-10-29 11:35:39",
                    "epoch_time": "2025-11-21"
                },
                "model_params": {
                    "use_mpc": True,
                    "use_cobs": True,
                    "use_astro": True,
                    "mpc_H": "11.8",
                    "mpc_n": "10",
                    "cobs_H": "6.8",
                    "cobs_n": "6.0",
                    "astro_H": "11.8",
                    "astro_n": "10"
                }
            },
            "C/2024 E1 (Wierzchos)": {
                "name": "C/2024 E1 (Wierzchos)",
                "orbit": {
                    "perihelion_distance": "0.566072",
                    "semi_major_axis": "",
                    "eccentricity": "1.000077",
                    "arg_perihelion": "243.6283",
                    "long_asc_node": "108.0793",
                    "inclination": "75.2398",
                    "orbital_period": "",
                    "perihelion_time": "2026-01-20 18:23:28",
                    "epoch_time": "2025-10-04"
                },
                "model_params": {
                    "use_mpc": True,
                    "use_cobs": True,
                    "use_astro": True,
                    "mpc_H": "7.0",
                    "mpc_n": "10",
                    "cobs_H": "7.6",
                    "cobs_n": "5.0",
                    "astro_H": "7.0",
                    "astro_n": "10"
                }
            }
        }

        if comet_name in comet_data:
            data = comet_data[comet_name]
            model_params = data["model_params"]

            self.clear_entries()
            self.name_entry.insert(0, data["name"])

            for key, value in data["orbit"].items():
                if value:
                    self.orbit_entries[key].insert(0, value)

            # 设置模型使用状态
            self.use_mpc_var.set(model_params["use_mpc"])
            self.use_cobs_var.set(model_params["use_cobs"])
            self.use_astro_var.set(model_params["use_astro"])

            # 填充模型参数
            self.mpc_H_entry.insert(0, model_params["mpc_H"])
            self.mpc_n_entry.insert(0, model_params["mpc_n"])
            self.cobs_H_entry.insert(0, model_params["cobs_H"])
            self.cobs_n_entry.insert(0, model_params["cobs_n"])
            self.astro_H_entry.insert(0, model_params["astro_H"])
            self.astro_n_entry.insert(0, model_params["astro_n"])

    def clear_entries(self):
        """清空所有输入框"""
        if hasattr(self, 'name_entry') and self.name_entry.winfo_exists():
            self.name_entry.delete(0, tk.END)

        for key, entry in self.orbit_entries.items():
            if entry is not None and hasattr(entry, 'winfo_exists') and entry.winfo_exists():
                entry.delete(0, tk.END)

        # 清空模型参数
        mag_entries = [
            self.mpc_H_entry, self.mpc_n_entry,
            self.cobs_H_entry, self.cobs_n_entry,
            self.astro_H_entry, self.astro_n_entry,
            self.query_date_entry
        ]

        for entry in mag_entries:
            if entry is not None and hasattr(entry, 'winfo_exists') and entry.winfo_exists():
                entry.delete(0, tk.END)

        # 更新结果显示
        for label in self.info_labels.values():
            label.config(text="待计算")

    def validate_inputs(self):
        """验证输入数据"""
        try:
            # 获取基本输入
            comet_name = self.name_entry.get().strip()
            if not comet_name:
                raise ValueError("请输入彗星名称")

            # 获取轨道参数
            q = float(self.orbit_entries["perihelion_distance"].get())
            e = float(self.orbit_entries["eccentricity"].get())
            peri_time_str = self.orbit_entries["perihelion_time"].get()

            # 计算半长轴
            a_str = self.orbit_entries["semi_major_axis"].get()
            if a_str:
                a = float(a_str)
            else:
                a = self.calculate_semi_major_axis(q, e)

            # 获取模型参数
            mpc_H = self.mpc_H_entry.get()
            mpc_n = self.mpc_n_entry.get()
            cobs_H = self.cobs_H_entry.get()
            cobs_n = self.cobs_n_entry.get()
            astro_H = self.astro_H_entry.get()
            astro_n = self.astro_n_entry.get()

            # 验证参数范围
            if not 0.001 <= q <= 1000:
                raise ValueError("近日点距离应在0.001-1000AU范围内")
            if e < 0:
                raise ValueError("偏心率不能为负")

            # 验证模型参数
            if self.use_mpc_var.get() and mpc_H and mpc_n:
                if not -50 <= float(mpc_H) <= 5000 or not -50 <= float(mpc_n) <= 5000:
                    raise ValueError("MPC模型参数应在-50到5000范围内")
            if self.use_cobs_var.get() and cobs_H and cobs_n:
                if not -50 <= float(cobs_H) <= 5000 or not -50 <= float(cobs_n) <= 5000:
                    raise ValueError("COBS模型参数应在-50到5000范围内")
            if self.use_astro_var.get() and astro_H and astro_n:
                if not -50 <= float(astro_H) <= 5000 or not -50 <= float(astro_n) <= 5000:
                    raise ValueError("ASTRO模型参数应在-50到5000范围内")

            # 解析近日点时间
            peri_date = datetime.strptime(peri_time_str, "%Y-%m-%d %H:%M:%S")

            return (comet_name, q, a, e, peri_date,
                    mpc_H, mpc_n, cobs_H, cobs_n, astro_H, astro_n)

        except Exception as e:
            messagebox.showerror("输入错误", str(e))
            return None

    def calculate_semi_major_axis(self, q, e):
        """计算半长轴"""
        if e < 1:
            return q / (1 - e)
        elif e == 1:
            return 1e10  # 使用极大值代表无穷大
        else:
            return q / (1 - e)

    def run_prediction(self):
        """运行亮度预测"""
        inputs = self.validate_inputs()
        if inputs is None:
            return

        (comet_name, q, a, e, peri_date,
         mpc_H, mpc_n, cobs_H, cobs_n, astro_H, astro_n) = inputs

        try:
            if self.pred_mode.get() == "days":
                future_days = int(self.pred_days_entry.get())
                if not 1 <= future_days <= 1000000:
                    raise ValueError("预测天数应在1-1000000范围内")

                # 生成时间序列（以近日点为中心）
                start_date = peri_date - timedelta(days=future_days//2)
                all_dates = [start_date +
                             timedelta(days=i) for i in range(future_days)]
            else:
                start_date_str = self.start_date_entry.get()
                end_date_str = self.end_date_entry.get()

                if not start_date_str or not end_date_str:
                    raise ValueError("请输入开始和结束日期")

                start_date = datetime.strptime(start_date_str, '%Y-%m-%d')
                end_date = datetime.strptime(end_date_str, '%Y-%m-%d')

                if start_date >= end_date:
                    raise ValueError("结束日期必须晚于开始日期")

                delta_days = (end_date - start_date).days
                if delta_days > 1000000:
                    raise ValueError("预测范围不能超过1000000天")

                all_dates = [start_date +
                             timedelta(days=i) for i in range(delta_days + 1)]

            # 确保包含今天的日期
            today = datetime.now().date()
            if today < all_dates[0].date() or today > all_dates[-1].date():
                messagebox.showwarning("日期范围警告", "预测范围不包含今天的日期")

            # 计算轨道距离
            r_pred = []
            for date in all_dates:
                days_from_peri = (date - peri_date).days

                if e < 1:
                    M = 2 * np.pi * days_from_peri / (a**1.5 * 365.25)
                    E = self.solve_kepler_equation(M, e)
                    r = a * (1 - e * np.cos(E))
                elif e == 1:
                    r = self.calculate_parabolic_orbit(days_from_peri, q)
                else:
                    r = self.calculate_hyperbolic_orbit(
                        days_from_peri, q, e, a)

                r_pred.append(max(r, 0.001))

            r_pred = np.array(r_pred)

            # 计算地彗距
            delta_pred = []
            for i, date in enumerate(all_dates):
                delta = self.calculate_earth_distance(
                    date, r_pred[i], e, peri_date)
                delta_pred.append(delta)
            delta_pred = np.array(delta_pred)

            # 计算三种模型的星等
            self.mpc_m_pred = None
            self.cobs_m_pred = None
            self.astro_m_pred = None

            if self.use_mpc_var.get() and mpc_H and mpc_n:
                self.mpc_m_pred = float(
                    mpc_H) + 5 * np.log10(delta_pred) + 2.5 * float(mpc_n) * np.log10(r_pred)
                self.mpc_m_pred = np.clip(self.mpc_m_pred, -100, 5000)

            if self.use_cobs_var.get() and cobs_H and cobs_n:
                self.cobs_m_pred = float(
                    cobs_H) + 5 * np.log10(delta_pred) + 2.5 * float(cobs_n) * np.log10(r_pred)
                self.cobs_m_pred = np.clip(self.cobs_m_pred, -100, 5000)

            if self.use_astro_var.get() and astro_H and astro_n:
                self.astro_m_pred = float(
                    astro_H) + 5 * np.log10(delta_pred) + 2.5 * float(astro_n) * np.log10(r_pred)
                self.astro_m_pred = np.clip(self.astro_m_pred, -100, 5000)

            # 存储当前数据
            self.current_dates = all_dates
            self.r_pred = r_pred
            self.delta_pred = delta_pred

            # 运行轨道模拟
            self.run_orbit_simulation()

            # 更新图表窗口
            self.update_brightness_window()
            self.update_distance_window()

            # 更新结果信息
            self.update_results(comet_name, e, all_dates,
                                r_pred, len(all_dates))

            messagebox.showinfo("预测完成", "彗星亮度预测计算完成！(v6.1)")

        except Exception as e:
            messagebox.showerror("预测错误", str(e))

    def solve_kepler_equation(self, M, e, tol=1e-8, max_iter=100):
        """解开普勒方程（椭圆轨道）"""
        if abs(M) < 1e-6:
            return M

        if e < 0.8:
            E = M
        else:
            E = np.pi if M > 0 else -np.pi

        for _ in range(max_iter):
            delta_E = (E - e * np.sin(E) - M) / (1 - e * np.cos(E))
            E -= delta_E

            if abs(E) > 10*np.pi:
                E = np.sign(E) * 10*np.pi

            if abs(delta_E) < tol:
                break

        return E

    def calculate_parabolic_orbit(self, days_from_peri, q):
        """计算抛物线轨道距离"""
        W = 3 * days_from_peri * np.sqrt(0.5 / (q**3)) / 2
        s = self.solve_cubic_equation(W)
        r = q * (1 + s**2)
        return max(r, 0.001)

    def solve_cubic_equation(self, W):
        """解三次方程 s + s^3/3 = W (抛物线轨道)"""
        s = W
        for _ in range(10):
            s_new = W - s**3 / 3
            if abs(s_new - s) < 1e-8:
                break
            s = s_new
        return s

    def calculate_hyperbolic_orbit(self, days_from_peri, q, e, a):
        """计算双曲线轨道距离"""
        N = 2 * np.pi * days_from_peri / ((-a)**1.5 * 365.25)
        H = self.solve_hyperbolic_kepler_equation(N, e)
        r = a * (1 - e * np.cosh(H))
        return max(r, 0.001)

    def solve_hyperbolic_kepler_equation(self, N, e, tol=1e-8, max_iter=100):
        """解双曲线开普勒方程"""
        if abs(N) < 0.1:
            H = N
        else:
            H = np.sign(N) * np.log(2*abs(N)/e + 1.8)

        for _ in range(max_iter):
            sinhH = np.sinh(H)
            coshH = np.cosh(H)

            f = e * sinhH - H - N
            df = e * coshH - 1

            if abs(df) < 1e-12:
                H += 0.1
                continue

            delta = f / df
            H -= delta

            if abs(H) > 100:
                H = np.sign(H) * 100

            if abs(delta) < tol:
                break

        return H

    def calculate_earth_distance(self, date, r, e, peri_date):
        """计算地彗距（简化模型）"""
        days_from_peri = (date - peri_date).days
        earth_angle = 2 * np.pi * (days_from_peri % 365.25) / 365.25

        # 地球位置（近似圆形轨道）
        earth_x = np.cos(earth_angle)
        earth_y = np.sin(earth_angle)

        # 彗星位置（简化计算）
        omega = np.radians(float(self.orbit_entries["arg_perihelion"].get()))
        i = np.radians(float(self.orbit_entries["inclination"].get()))

        comet_x = r * np.cos(omega)
        comet_y = r * np.sin(omega) * np.cos(i)

        # 计算距离
        delta = np.sqrt((comet_x - earth_x)**2 + (comet_y - earth_y)**2)
        return max(delta, 0.001)

    def run_orbit_simulation(self):
        """运行轨道模拟"""
        inputs = self.validate_inputs()
        if inputs is None:
            return

        comet_name, q, a, e, peri_date, _, _, _, _, _, _ = inputs

        try:
            num_points = 500
            sim_years = 10  # 10年时间范围

            days_range = sim_years * 365.25
            start_date = peri_date - timedelta(days=days_range/2)
            end_date = peri_date + timedelta(days=days_range/2)

            dates = [start_date + timedelta(days=i*days_range/num_points)
                     for i in range(num_points)]

            positions = []
            for date in dates:
                days_from_peri = (date - peri_date).days

                if e < 1:
                    M = 2 * np.pi * days_from_peri / (a**1.5 * 365.25)
                    E = self.solve_kepler_equation(M, e)
                    r = a * (1 - e * np.cos(E))
                elif e == 1:
                    r = self.calculate_parabolic_orbit(days_from_peri, q)
                else:
                    r = self.calculate_hyperbolic_orbit(
                        days_from_peri, q, e, a)

                omega = np.radians(
                    float(self.orbit_entries["arg_perihelion"].get()))
                Omega = np.radians(
                    float(self.orbit_entries["long_asc_node"].get()))
                i = np.radians(float(self.orbit_entries["inclination"].get()))

                if e < 1:
                    E_val = self.solve_kepler_equation(
                        2 * np.pi * days_from_peri / (a**1.5 * 365.25), e)
                    # 3D坐标计算
                    x = r * (np.cos(Omega)*np.cos(omega+E_val) -
                             np.sin(Omega)*np.sin(omega+E_val)*np.cos(i))
                    y = r * (np.sin(Omega)*np.cos(omega+E_val) +
                             np.cos(Omega)*np.sin(omega+E_val)*np.cos(i))
                    z = r * np.sin(omega+E_val) * np.sin(i)
                else:
                    # 简化计算（双曲线/抛物线）
                    theta = 2 * np.arctan(np.sqrt((1+e)/(1-e)) * np.tanh(self.solve_hyperbolic_kepler_equation(
                        2 * np.pi * days_from_peri / 365.25, e)/2)) if e > 1 else 2 * np.pi * days_from_peri / 365.25
                    x = r * np.cos(theta)
                    y = r * np.sin(theta) * np.cos(i)
                    z = r * np.sin(theta) * np.sin(i)

                positions.append([x, y, z])

            self.orbit_points = np.array(positions)

            # 更新3D轨道显示
            self.update_orbit_3d_display()

        except Exception as e:
            messagebox.showerror("轨道模拟错误", f"轨道模拟失败: {str(e)}")

    def update_results(self, comet_name, e, all_dates, r_pred, total_days):
        """更新预测结果信息"""
        # 确定轨道类型
        if e < 1:
            orbit_type = f"椭圆轨道 (e={e:.6f})"
        elif e == 1:
            orbit_type = "抛物线轨道 (e=1.000000)"
        else:
            orbit_type = f"双曲线轨道 (e={e:.6f})"

        # 更新基本信息
        self.info_labels["轨道类型"].config(text=orbit_type)
        self.info_labels["预测总天数"].config(text=f"{total_days:,} 天")

        # 查找近日点
        peri_idx = np.argmin(r_pred)
        peri_date = all_dates[peri_idx].strftime('%Y-%m-%d')
        peri_dist = r_pred[peri_idx]
        self.info_labels["近日点日期"].config(text=peri_date)
        self.info_labels["近地点距离"].config(text=f"{peri_dist:.6f} AU")

        # 查找今日数据
        today = datetime.now().date()
        today_idx = None
        for i, date in enumerate(all_dates):
            if date.date() == today:
                today_idx = i
                break

        if today_idx is not None:
            today_data = {
                "mpc": self.mpc_m_pred[today_idx] if self.mpc_m_pred is not None else "N/A",
                "cobs": self.cobs_m_pred[today_idx] if self.cobs_m_pred is not None else "N/A",
                "astro": self.astro_m_pred[today_idx] if self.astro_m_pred is not None else "N/A",
                "r": r_pred[today_idx],
                "delta": self.delta_pred[today_idx]
            }

            self.info_labels["今日日彗距"].config(text=f"{today_data['r']:.6f}")
            self.info_labels["今日地彗距"].config(text=f"{today_data['delta']:.6f}")

            if today_data['mpc'] != "N/A":
                self.info_labels["今日MPC星等"].config(
                    text=f"{today_data['mpc']:.2f}")
            if today_data['cobs'] != "N/A":
                self.info_labels["今日COBS星等"].config(
                    text=f"{today_data['cobs']:.2f}")
            if today_data['astro'] != "N/A":
                self.info_labels["今日ASTRO星等"].config(
                    text=f"{today_data['astro']:.2f}")

        # 更新30天和60天数据
        for days in [30, 60]:
            future_date = datetime.now() + timedelta(days=days)
            future_idx = None
            for i, date in enumerate(all_dates):
                if date.date() == future_date.date():
                    future_idx = i
                    break

            if future_idx is not None:
                future_data = {
                    "mpc": self.mpc_m_pred[future_idx] if self.mpc_m_pred is not None else "N/A",
                    "cobs": self.cobs_m_pred[future_idx] if self.cobs_m_pred is not None else "N/A",
                    "astro": self.astro_m_pred[future_idx] if self.astro_m_pred is not None else "N/A",
                    "r": r_pred[future_idx],
                    "delta": self.delta_pred[future_idx]
                }

                self.info_labels[f"{days}天后日彗距"].config(
                    text=f"{future_data['r']:.6f}")
                self.info_labels[f"{days}天后地彗距"].config(
                    text=f"{future_data['delta']:.6f}")

                if future_data['mpc'] != "N/A":
                    self.info_labels[f"{days}天后MPC星等"].config(
                        text=f"{future_data['mpc']:.2f}")
                if future_data['cobs'] != "N/A":
                    self.info_labels[f"{days}天后COBS星等"].config(
                        text=f"{future_data['cobs']:.2f}")
                if future_data['astro'] != "N/A":
                    self.info_labels[f"{days}天后ASTRO星等"].config(
                        text=f"{future_data['astro']:.2f}")

        # 计算彗发和彗尾长度
        if today_idx is not None:
            coma_length_km, coma_length_deg, tail_length_km, tail_length_deg = self.calculate_coma_tail_length(
                today_data["r"], today_data["delta"])
            self.info_labels["彗发长度 (km)"].config(text=f"{coma_length_km:.0f}")
            self.info_labels["彗发长度 (°)"].config(text=f"{coma_length_deg:.4f}")
            self.info_labels["彗尾长度 (km)"].config(text=f"{tail_length_km:.0f}")
            self.info_labels["彗尾长度 (°)"].config(text=f"{tail_length_deg:.4f}")

        self.root.update_idletasks()

    def calculate_coma_tail_length(self, r, delta):
        """计算彗发和彗尾长度"""
        # 彗发长度（km） - 与距离太阳的距离成反比
        coma_length_km = 100000 / (r ** 0.5)

        # 彗尾长度（km） - 通常比彗发长
        tail_length_km = 1000000 / (r ** 0.5)

        # 转换为角直径（度）
        # 1 AU = 149,597,870.7 km
        au_to_km = 149597870.7
        delta_km = delta * au_to_km

        coma_length_deg = np.degrees(coma_length_km / delta_km)
        tail_length_deg = np.degrees(tail_length_km / delta_km)

        return coma_length_km, coma_length_deg, tail_length_km, tail_length_deg

    def open_brightness_window(self):
        """打开亮度图表窗口"""
        if self.brightness_window is None or not self.brightness_window.winfo_exists():
            self.brightness_window = tk.Toplevel(self.root)
            self.brightness_window.title("彗星亮度预测图表 v6.1")
            self.setup_brightness_window()

            if self.current_dates is not None:
                self.update_brightness_window()
        else:
            self.brightness_window.lift()

    def open_distance_window(self):
        """打开距离图表窗口"""
        if self.distance_window is None or not self.distance_window.winfo_exists():
            self.distance_window = tk.Toplevel(self.root)
            self.distance_window.title("彗星距离变化图表 v6.1")
            self.setup_distance_window()

            if self.current_dates is not None:
                self.update_distance_window()
        else:
            self.distance_window.lift()

    def open_orbit_3d_window(self):
        """打开3D轨道窗口"""
        if self.orbit_3d_window is None or not self.orbit_3d_window.winfo_exists():
            self.orbit_3d_window = tk.Toplevel(self.root)
            self.orbit_3d_window.title("彗星3D轨道模拟 v6.1")
            self.setup_orbit_3d_window()

            if self.orbit_points is not None:
                self.update_orbit_3d_display()
        else:
            self.orbit_3d_window.lift()

    def close_all_windows(self):
        """关闭所有子窗口"""
        if self.brightness_window is not None and self.brightness_window.winfo_exists():
            self.brightness_window.destroy()
        if self.distance_window is not None and self.distance_window.winfo_exists():
            self.distance_window.destroy()
        if self.orbit_3d_window is not None and self.orbit_3d_window.winfo_exists():
            self.orbit_3d_window.destroy()

    def setup_brightness_window(self):
        """设置亮度图表窗口"""
        if not self.brightness_window.winfo_exists():
            return

        self.brightness_fig, self.brightness_ax = plt.subplots(figsize=(12, 8))
        self.brightness_canvas = FigureCanvasTkAgg(
            self.brightness_fig, master=self.brightness_window)
        self.brightness_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)

        toolbar = NavigationToolbar2Tk(
            self.brightness_canvas, self.brightness_window)
        toolbar.update()

    def setup_distance_window(self):
        """设置距离图表窗口"""
        if not self.distance_window.winfo_exists():
            return

        self.distance_fig, self.distance_ax = plt.subplots(figsize=(12, 8))
        self.distance_canvas = FigureCanvasTkAgg(
            self.distance_fig, master=self.distance_window)
        self.distance_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)

        toolbar = NavigationToolbar2Tk(
            self.distance_canvas, self.distance_window)
        toolbar.update()

    def setup_orbit_3d_window(self):
        """设置3D轨道窗口"""
        if not self.orbit_3d_window.winfo_exists():
            return

        # 创建3D图形
        self.orbit_3d_fig = plt.figure(figsize=(12, 8))
        self.orbit_3d_ax = self.orbit_3d_fig.add_subplot(111, projection='3d')
        self.orbit_3d_canvas = FigureCanvasTkAgg(
            self.orbit_3d_fig, master=self.orbit_3d_window)
        self.orbit_3d_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)

        # 添加工具栏
        toolbar = NavigationToolbar2Tk(
            self.orbit_3d_canvas, self.orbit_3d_window)
        toolbar.update()

    def update_brightness_window(self):
        """更新亮度图表窗口内容"""
        if (self.brightness_window is None or
            not self.brightness_window.winfo_exists() or
                self.current_dates is None):
            return

        self.brightness_ax.clear()

        e = float(self.orbit_entries["eccentricity"].get())
        if e < 1:
            orbit_type = "椭圆轨道"
        elif e == 1:
            orbit_type = "抛物线轨道"
        else:
            orbit_type = "双曲线轨道"

        colors = ['red', 'blue', 'green']
        labels = ['MPC模型', 'COBS模型', 'ASTRO模型']
        predictions = [self.mpc_m_pred, self.cobs_m_pred, self.astro_m_pred]
        use_models = [self.use_mpc_var.get(), self.use_cobs_var.get(),
                      self.use_astro_var.get()]

        for i, (pred, color, label, use) in enumerate(zip(predictions, colors, labels, use_models)):
            if pred is not None and use:
                self.brightness_ax.plot(self.current_dates, pred, color=color,
                                        label=label, linewidth=2)

                min_mag = min(pred)
                min_idx = np.argmin(pred)
                self.brightness_ax.plot(self.current_dates[min_idx], min_mag, 'o',
                                        color=color, markersize=8,
                                        label=f'{label}最亮: {min_mag:.2f}等')

        today = datetime.now().date()
        today_idx = None
        for i, date in enumerate(self.current_dates):
            if date.date() == today:
                today_idx = i
                break

        if today_idx is not None:
            for i, (pred, color, label, use) in enumerate(zip(predictions, colors, labels, use_models)):
                if pred is not None and use:
                    today_mag = pred[today_idx]
                    self.brightness_ax.plot(self.current_dates[today_idx], today_mag, 's',
                                            color=color, markersize=6,
                                            label=f'今日{label}: {today_mag:.2f}等')

        # 标记30天后位置
        thirty_days_later = datetime.now() + timedelta(days=30)
        thirty_idx = None
        for i, date in enumerate(self.current_dates):
            if date.date() == thirty_days_later.date():
                thirty_idx = i
                break

        if thirty_idx is not None:
            for i, (pred, color, label, use) in enumerate(zip(predictions, colors, labels, use_models)):
                if pred is not None and use:
                    thirty_mag = pred[thirty_idx]
                    self.brightness_ax.plot(self.current_dates[thirty_idx], thirty_mag, 'D',
                                            color=color, markersize=6,
                                            label=f'30天后{label}: {thirty_mag:.2f}等')

        self.brightness_ax.set_ylabel("视星等")
        self.brightness_ax.set_title(
            f"{self.comet_var.get()}亮度预测 - {orbit_type} (v6.1)", fontsize=14)
        self.brightness_ax.grid(True, alpha=0.3)
        self.brightness_ax.legend()
        self.brightness_ax.invert_yaxis()

        self.brightness_fig.autofmt_xdate()
        self.brightness_fig.tight_layout()
        self.brightness_canvas.draw()

    def update_distance_window(self):
        """更新距离图表窗口内容"""
        if (self.distance_window is None or
            not self.distance_window.winfo_exists() or
                self.current_dates is None):
            return

        self.distance_ax.clear()

        e = float(self.orbit_entries["eccentricity"].get())
        if e < 1:
            orbit_type = "椭圆轨道"
        elif e == 1:
            orbit_type = "抛物线轨道"
        else:
            orbit_type = "双曲线轨道"

        self.distance_ax.plot(self.current_dates, self.r_pred,
                              'red', label='日彗距', linewidth=2)
        self.distance_ax.plot(self.current_dates, self.delta_pred, 'blue',
                              label='地彗距', linewidth=2)

        today = datetime.now().date()
        today_idx = None
        for i, date in enumerate(self.current_dates):
            if date.date() == today:
                today_idx = i
                break

        if today_idx is not None:
            today_r = self.r_pred[today_idx]
            today_delta = self.delta_pred[today_idx]
            self.distance_ax.plot(self.current_dates[today_idx], today_r, 's', color='purple',
                                  markersize=6, label=f'今日日彗距: {today_r:.6f} AU')
            self.distance_ax.plot(self.current_dates[today_idx], today_delta, 's', color='green',
                                  markersize=6, label=f'今日地彗距: {today_delta:.6f} AU')

        peri_date_str = self.orbit_entries["perihelion_time"].get()
        if peri_date_str:
            try:
                peri_date = datetime.strptime(
                    peri_date_str, "%Y-%m-%d %H:%M:%S").date()
                peri_idx = None
                for i, date in enumerate(self.current_dates):
                    if date.date() == peri_date:
                        peri_idx = i
                        break

                if peri_idx is not None:
                    peri_r = self.r_pred[peri_idx]
                    peri_delta = self.delta_pred[peri_idx]
                    self.distance_ax.plot(self.current_dates[peri_idx], peri_r, 'D', color='orange',
                                          markersize=6, label=f'近日点: {peri_r:.6f} AU')
                    self.distance_ax.plot(self.current_dates[peri_idx], peri_delta, 'D', color='cyan',
                                          markersize=6, label=f'近日点地彗距: {peri_delta:.6f} AU')
            except ValueError:
                pass

        self.distance_ax.set_ylabel("距离 (AU)")
        self.distance_ax.set_xlabel("日期")
        self.distance_ax.set_title(
            f"{self.comet_var.get()}距离变化 - {orbit_type} (v6.1)", fontsize=14)
        self.distance_ax.grid(True, alpha=0.3)
        self.distance_ax.legend()

        self.distance_fig.autofmt_xdate()
        self.distance_fig.tight_layout()
        self.distance_canvas.draw()

    def update_orbit_3d_display(self):
        """更新3D轨道显示"""
        if (self.orbit_3d_window is None or
                not self.orbit_3d_window.winfo_exists()):
            return

        self.orbit_3d_ax.clear()

        # 绘制太阳
        self.orbit_3d_ax.scatter(
            [0], [0], [0], color='yellow', s=200, label='太阳')

        # 绘制彗星轨道（如果已计算）
        if self.orbit_points is not None:
            x, y, z = self.orbit_points.T
            self.orbit_3d_ax.plot(
                x, y, z, 'b-', linewidth=2, label='彗星轨道', alpha=0.8)

            # 标记今日位置
            today = datetime.now().date()
            today_idx = None
            for i, date in enumerate(self.current_dates):
                if date.date() == today:
                    today_idx = i
                    break

            if today_idx is not None and today_idx < len(x):
                today_x, today_y, today_z = x[today_idx], y[today_idx], z[today_idx]
                self.orbit_3d_ax.scatter([today_x], [today_y], [today_z], color='green', s=100,
                                         label=f'今日位置\n日彗距: {self.r_pred[today_idx]:.3f} AU')

            # 标记30天后位置
            thirty_days_later = datetime.now() + timedelta(days=30)
            thirty_idx = None
            for i, date in enumerate(self.current_dates):
                if date.date() == thirty_days_later.date():
                    thirty_idx = i
                    break

            if thirty_idx is not None and thirty_idx < len(x):
                thirty_x, thirty_y, thirty_z = x[thirty_idx], y[thirty_idx], z[thirty_idx]
                self.orbit_3d_ax.scatter([thirty_x], [thirty_y], [thirty_z], color='orange', s=100,
                                         label=f'30天后位置\n日彗距: {self.r_pred[thirty_idx]:.3f} AU')

            # 标记近日点
            if len(x) > 0:
                peri_idx = np.argmin(np.linalg.norm(self.orbit_points, axis=1))
                peri_x, peri_y, peri_z = x[peri_idx], y[peri_idx], z[peri_idx]
                self.orbit_3d_ax.scatter([peri_x], [peri_y], [peri_z], color='red', s=100,
                                         label=f'近日点\n距离: {self.r_pred[peri_idx]:.3f} AU')

        # 设置标签和标题
        e = float(self.orbit_entries["eccentricity"].get())
        orbit_type = "椭圆" if e < 1 else "抛物线" if e == 1 else "双曲线"
        self.orbit_3d_ax.set_title(
            f"{self.comet_var.get()} 3D轨道模拟 ({orbit_type}轨道) v6.1", fontsize=14)

        self.orbit_3d_ax.set_xlabel('X (AU)')
        self.orbit_3d_ax.set_ylabel('Y (AU)')
        self.orbit_3d_ax.set_zlabel('Z (AU)')

        self.orbit_3d_ax.legend()
        self.orbit_3d_canvas.draw()

    def query_brightness(self):
        """查询指定日期的亮度"""
        inputs = self.validate_inputs()
        if inputs is None:
            return

        (comet_name, q, a, e, peri_date,
         mpc_H, mpc_n, cobs_H, cobs_n, astro_H, astro_n) = inputs

        try:
            # 获取查询日期
            query_date_str = self.query_date_entry.get()
            query_date = datetime.strptime(query_date_str, '%Y-%m-%d')

            # 计算轨道距离
            days_from_peri = (query_date - peri_date).days

            if e < 1:
                # 椭圆轨道 - 使用完整的开普勒方程
                M = 2 * np.pi * days_from_peri / (a**1.5 * 365.25)
                E = self.solve_kepler_equation(M, e)
                r = a * (1 - e * np.cos(E))
            elif e == 1:
                # 抛物线轨道 - 使用精确计算
                r = self.calculate_parabolic_orbit(days_from_peri, q)
            else:
                # 双曲线轨道 - 使用完整算法
                r = self.calculate_hyperbolic_orbit(days_from_peri, q, e, a)

            # 计算地彗距
            delta = self.calculate_earth_distance(query_date, r, e, peri_date)

            # 计算三种模型的星等
            results = {}
            if self.use_mpc_var.get() and mpc_H and mpc_n:
                mpc_m = float(mpc_H) + 5 * np.log10(delta) + \
                    2.5 * float(mpc_n) * np.log10(r)
                results["MPC"] = mpc_m
            if self.use_cobs_var.get() and cobs_H and cobs_n:
                cobs_m = float(cobs_H) + 5 * np.log10(delta) + \
                    2.5 * float(cobs_n) * np.log10(r)
                results["COBS"] = cobs_m
            if self.use_astro_var.get() and astro_H and astro_n:
                astro_m = float(astro_H) + 5 * np.log10(delta) + \
                    2.5 * float(astro_n) * np.log10(r)
                results["ASTRO"] = astro_m

            # 计算彗发和彗尾长度
            coma_length_km, coma_length_deg, tail_length_km, tail_length_deg = self.calculate_coma_tail_length(
                r, delta)

            # 显示结果
            orbit_type = "椭圆" if e < 1 else "抛物线" if e == 1 else "双曲线"
            result_text = (
                f"彗星: {comet_name} ({orbit_type}轨道) v6.1\n"
                f"查询日期: {query_date_str}\n"
                f"日彗距: {r:.6f} AU\n"
                f"地彗距: {delta:.6f} AU\n"
            )

            for model, mag in results.items():
                result_text += f"{model}预测: {mag:.2f} 等\n"

            result_text += (
                f"彗发长度: {coma_length_km:.0f} km ({coma_length_deg:.4f}°)\n"
                f"彗尾长度: {tail_length_km:.0f} km ({tail_length_deg:.4f}°)"
            )
            messagebox.showinfo("查询结果", result_text)

        except Exception as e:
            messagebox.showerror("查询错误", str(e))

    def show_future_data(self):
        """显示未来数据（30天/60天/自定义）"""
        if self.current_dates is None:
            messagebox.showinfo("提示", "请先运行预测计算")
            return

        option = self.future_days_option.get()
        if option == '自定义':
            try:
                days = int(self.custom_days_entry.get())
            except:
                messagebox.showerror("错误", "请输入有效的天数")
                return
        elif option == '30天':
            days = 30
        else:  # 60天
            days = 60

        future_date = datetime.now() + timedelta(days=days)
        future_idx = None
        for i, date in enumerate(self.current_dates):
            if date.date() == future_date.date():
                future_idx = i
                break

        if future_idx is None:
            messagebox.showinfo("提示", f"{days}天后不在预测范围内")
            return

        # 显示消息框
        result_text = (
            f"{days}天后预测数据 ({future_date.strftime('%Y-%m-%d')}):\n"
            f"日彗距: {self.r_pred[future_idx]:.6f} AU\n"
            f"地彗距: {self.delta_pred[future_idx]:.6f} AU\n"
        )

        if self.mpc_m_pred is not None:
            result_text += f"MPC星等: {self.mpc_m_pred[future_idx]:.2f}\n"
        if self.cobs_m_pred is not None:
            result_text += f"COBS星等: {self.cobs_m_pred[future_idx]:.2f}\n"
        if self.astro_m_pred is not None:
            result_text += f"ASTRO星等: {self.astro_m_pred[future_idx]:.2f}\n"

        messagebox.showinfo(f"{days}天后预测数据", result_text)


if __name__ == "__main__":
    root = tk.Tk()
    app = CometBrightnessApp(root)
    root.mainloop()
